﻿using System;
using System.Linq;
using System.Collections.Generic;

using SharedGenomics.Core;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Annotations
{
    public class AnnotationRequestContext
    {
        private readonly IList<AnnotationValueArtifact> _valueList;
        private readonly IDictionary<AnnotationKey, AnnotationResultArtifact> _annotations;
        private readonly HashSet<AnnotationKey> _failedRequests;
        private AnnotationKey _requestKey;

        public AnnotationRequestContext(AnnotationKey key, object annotationTarget)
        {
            annotationTarget.ThrowIfNull("annotationTarget");

            this.RequestKey = key;
            this.AnnotationTarget = annotationTarget;

            this._annotations = new Dictionary<AnnotationKey, AnnotationResultArtifact>();
            this._failedRequests = new HashSet<AnnotationKey>();
            this._valueList = new List<AnnotationValueArtifact>();
        }

        public void AddValue(AnnotationValue value, AnnotationValue parentValue)
        {
            value.ThrowIfNull("value");
            this._valueList.Add(new AnnotationValueArtifact(value, parentValue));
        }

        public void AddAnnotation(IAnnotation annotation, AnnotationValue parentValue)
        {
            annotation.ThrowIfNull("annotation");

            AnnotationKey key = annotation.AnnotationKey;

            this._annotations[key] = new AnnotationResultArtifact(annotation, parentValue);
            this._failedRequests.Remove(key);
        }

        public void FailRequest()
        {
            this.FailRequest(this.RequestKey);
        }

        public void FailRequest(AnnotationKey key)
        {
            key.ThrowIfNull("key");

            if (!this._annotations.ContainsKey(key))
            {
                this._failedRequests.Add(key);
            }
        }

        public bool ContainsResultFor(AnnotationKey key)
        {
            //true if the request has been failed or succeeded
            return key != null && (this._failedRequests.Contains(key) || this._annotations.ContainsKey(key));
        }

        public IEnumerable<AnnotationRequestResult> Results
        {
            get
            {
                var succeeded = this._annotations
                    .Select(kvp => new SucceededAnnotationRequestResult(kvp.Key, this.AnnotationTarget, kvp.Value.Annotation, null))
                    .Cast<AnnotationRequestResult>();

                var failed = this._failedRequests
                    .Select(key => new FailedAnnotationRequestResult(key, this.AnnotationTarget))
                    .Cast<AnnotationRequestResult>();

                return succeeded.Concat(failed);
            }
        }

        public IEnumerable<AnnotationArtifact> OutputArtifacts
        {
            get
            {
                return this._annotations.Values.Cast<AnnotationArtifact>()
                    .Concat(this._valueList.Cast<AnnotationArtifact>());
            }
        }

        public AnnotationKey RequestKey
        {
            get { return this._requestKey; }
            set
            {
                value.ThrowIfNull("RequestKey");
                this._requestKey = value;
            }
        }
        public object AnnotationTarget { get; private set; }
    }
}
